#include "mysql_pkg.h"
#include <assert.h>
#include "mysql_cmd.h"
#include "const.h"
#include "log_mgr.h"
#include "query_pkg.h"
#include "result_pkg.h"
#include "auth_pkg.h"
#include "my_sql_parse.h"

extern CSqlParser g_sqlParser;

CMysqlPkg::CMysqlPkg()
{
	m_pReqPkgs = new IPkg*[MAX_MYSQL_CMD];
	assert(m_pReqPkgs);
	for(int i=0; i<MAX_MYSQL_CMD; i++)
		m_pReqPkgs[i] = NULL;

	m_pRespPkgs = new IPkg*[MAX_MYSQL_CMD];
	assert(m_pRespPkgs);
	for(int i=0; i<MAX_MYSQL_CMD; i++)
		m_pRespPkgs[i] = NULL;
	//init pkg map
	m_pReqPkgs[MY_COM_QUERY] = new QueryPkg();
	assert(m_pReqPkgs[MY_COM_QUERY]);

	m_pRespPkgs[MY_COM_QUERY] = new ResultPkg();
	assert(m_pRespPkgs[MY_COM_QUERY]);

	//MY_COM_CONNECT is an internal thread state
	//reuse for client auth pkg
	m_pReqPkgs[MY_COM_CONNECT] = new AuthPkg();
	assert(m_pReqPkgs[MY_COM_CONNECT]);

	m_pRespPkgs[MY_COM_CONNECT] = new ResultPkg();
	assert(m_pRespPkgs[MY_COM_CONNECT]);
	//m_pSqlParser = new CSqlParser();
	//assert(m_pSqlParser);
	//assert(m_pSqlParser->Init());
}

CMysqlPkg::~CMysqlPkg()
{
	for(int i=0; i<MAX_MYSQL_CMD; i++)
	{
		if(m_pReqPkgs[i])
			delete m_pReqPkgs[i];
	}
	for(int i=0; i<MAX_MYSQL_CMD; i++)
	{
		if(m_pRespPkgs[i])
			delete m_pRespPkgs[i];
	}
	delete[] m_pReqPkgs;
	delete[] m_pRespPkgs;
	//m_pSqlParser->End();
	//delete m_pSqlParser;
}

void CMysqlPkg::ChangeStatus(Context* pCtx)
{
	assert(pCtx);
	switch(pCtx->pMysql->proto)
	{
		case CLOSED:
			assert(0);
		case REQ_CONNECT:
			assert(pCtx->type == M2M);
			pCtx->pMysql->proto = RESP_HANDSHAKE;
			break;
		case RESP_HANDSHAKE:
			assert(pCtx->type == C2M);
			pCtx->pMysql->proto = REQ_AUTH;
			break;
		case REQ_AUTH:
			assert(pCtx->type == M2M);
			pCtx->pMysql->proto = RESP_AUTH;
			break;
		case RESP_AUTH:
			if(pCtx->type == C2M)
				pCtx->pMysql->proto = REQ_QUERY;
			break;
		case REQ_QUERY:
			if(pCtx->type == C2M)
				pCtx->pMysql->proto = REQ_QUERY;
			else
				pCtx->pMysql->proto = RESP_QUERY;
			break;
		case RESP_QUERY:
			if(pCtx->type == C2M)
				pCtx->pMysql->proto = REQ_QUERY;
			else
				pCtx->pMysql->proto = RESP_QUERY;
			break;
		default:
			assert(0);
	}
	
}

Result CMysqlPkg::Parse(Context* pCtx)
{
	assert(pCtx);
	Result ret = OK;
#ifdef MASK_TEST
			return OK;
#endif
	
	this->ChangeStatus(pCtx);
	
	this->Dump(pCtx);

	ret = this->Check(pCtx);
	if(ret != OK)
		return ret;
	
	switch(pCtx->pMysql->proto)
	{
		case REQ_QUERY:
		{
			IPkg* pkg = m_pReqPkgs[CMD_NUM(pCtx->pMysql->pBuff)];
			if(pkg)
			{
				pCtx->pMysql->pReqPkg = pkg;
				pCtx->pMysql->pRespPkg = m_pRespPkgs[CMD_NUM(pCtx->pMysql->pBuff)];
				pCtx->pMysql->pParser = &g_sqlParser;
				return pkg->Parse(pCtx);
			}
			break;
		}
		case RESP_QUERY:
		{
			if(pCtx->pMysql->pRespPkg)
			{
				//pCtx->pMysql->pParser = pCtx->pMysql->pRespPkg;
				return pCtx->pMysql->pRespPkg->Parse(pCtx);
			}
			break;
		}
		case REQ_AUTH:
		{
			IPkg* pkg = m_pReqPkgs[MY_COM_CONNECT];
			if(pkg)
			{
				pCtx->pMysql->pParser = &g_sqlParser;
				return pkg->Parse(pCtx);
			}
			break;
		}
	case RESP_HANDSHAKE:
	case RESP_AUTH:
		{
			IPkg* pkg = m_pRespPkgs[MY_COM_CONNECT];
			if(pkg)
			{
				return pkg->Parse(pCtx);
			}
			break;
		}			
		default:
			break;
	}
	
	return ret;
}


Result CMysqlPkg::Check(Context* pCtx)
{	
	return OK;
#if 0
	assert(pCtx);
	Result ret = OK;

	if(pCtx->pMysql->proto==REQ_QUERY 
		&& !pkgCmdInfo[CMD_NUM(pCtx->pMysql->pBuff)].isAccept)
	{
		ret = ERR_DROP;
		PKG_ERROR("[%s] [req pkg|pkg cmd no:%s not support]", 
			retDesc[ret], 
			pkgCmdInfo[CMD_NUM(pCtx->pMysql->pBuff)].name);
		return ret;
	}
	return ret;
#endif
}

void CMysqlPkg::Dump(Context* pCtx)
{
	int iPkgLen = PKG_LEN(pCtx->pMysql->pBuff);

	PKG_HEX(pCtx->pMysql->pBuff+PKG_HEAD_LEN, iPkgLen);
	
	return; 
	
	switch(pCtx->pMysql->proto)
	{
		case REQ_CONNECT:
			PKG("[->][-]%s", myStatusDesc[pCtx->pMysql->proto]);
			break;
		case REQ_AUTH:
			PKG("[->][%d]%s", *(pCtx->pMysql->pBuff+PKG_NUM_OFFSET), myStatusDesc[pCtx->pMysql->proto]);
			break;
		case RESP_HANDSHAKE:
		case RESP_AUTH:
		case RESP_QUERY:
			PKG("[<-][%d]%s", *(pCtx->pMysql->pBuff+PKG_NUM_OFFSET), myStatusDesc[pCtx->pMysql->proto]);
			break;
		case REQ_QUERY:
			PKG("[->][%d]%s,Cmd:%s", *(pCtx->pMysql->pBuff+PKG_NUM_OFFSET), myStatusDesc[pCtx->pMysql->proto], 
				pkgCmdInfo[CMD_NUM(pCtx->pMysql->pBuff)].name);
			break;
		case CLOSED:
			PKG("[%s][-]%s", ((pCtx->type==C2M)?"->":"<-"), myStatusDesc[pCtx->pMysql->proto]);
			break;
		default:
			assert(0);
	}
}

